/*
PearReview - The peer review assistant.
Copyright (C) 2009  Dimitrij Pankratz, Anton Musichin
http://www.pearreview.com, info@pearreview.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.pearreview.moderatorplugin;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.pearreview.application.Messages;
import com.pearreview.util.XMLFileFilter;
import com.pearreview.util.review.Attendee;
import com.pearreview.util.review.Meeting;
import com.pearreview.util.review.Review;
import com.pearreview.util.review.io.ReviewWriter;
import com.pearreview.util.review.io.XMLReviewWriter;

public class ZipInvitationPackageCreator extends AbstractInvitationCreator 
		implements InvitationPackageCreator {
	private static final long serialVersionUID = -1511924079240449978L;
	
	public static final String ZIP_EXTANSION = ".zip";
	public static final int READ_FILE_BUFFER_SIZE = 4 * 1024;
	
	private boolean attachReferenceFiles;
	private boolean attachReviewFile;
	
	/**
	 * Returns localized string of this component
	 * @param key key of string to localize
	 * @return localized string of this component
	 */
	private static String getLocalizedString(String key) {
		return Messages.getString("PearReview.invitation." + key);
	}
	
	public ZipInvitationPackageCreator() {
		this(true);
	}

	public ZipInvitationPackageCreator(boolean attachReferenceFiles) {
		this(attachReferenceFiles, true);
	}
	
	public ZipInvitationPackageCreator(boolean attachReferenceFiles, 
			boolean attachReviewFile) {
		setAttachReferenceFiles(attachReferenceFiles);
		setAttachReviewFile(attachReviewFile);
	}
	
	@Override
	public void create(Review review, List<Meeting> meetings, File dir,
			InvitationDocumentCreator creator) throws Exception {
		
		if(review.getAttendees() == null)
			return;
		
		for(Attendee attendee: review.getAttendees()) {
			File attendeeDir = new File(dir, attendee.getName());
			File attendeeZip = new File(attendeeDir, 
					getLocalizedString("invitation") + ZIP_EXTANSION);
			attendeeZip = getFreeFile(attendeeZip);
			attendeeDir.mkdirs();
			
			ZipOutputStream out = null;
			try {
				out = new ZipOutputStream(new FileOutputStream(attendeeZip));
				
				out.setComment(getLocalizedString("invitation.package"));
				
				// compression level
				out.setLevel(Deflater.DEFAULT_COMPRESSION);
				
				ZipEntry entry = new ZipEntry(getLocalizedString("invitation") + 
						creator.getFileExtension());
				out.putNextEntry(entry);
				
				creator.create(review, meetings, attendee, new ZipStream(out));
				
				// attach review files
				if(isAttachReferenceFiles() && review.getProduct() != null) {
					attachReferenceFiles(out, 
							review.getProduct().getReference());
				}
				
				if(this.isAttachReviewFile() && review != null) {
					attachReviewFile(out, review);
				}
				
			} catch(Exception e) {
				throw e;
			} finally {
				try {
					if(out != null)
						out.close();
				} catch(Throwable e) {
					
				}
			}
		}
	}
	
	/**
	 * Attaches reference files to zip output stream
	 * @param zipStream
	 * @param lst
	 * @throws IOException 
	 */
	protected void attachReferenceFiles(
			ZipOutputStream zipStream, List<String> lst) throws IOException {
		if(lst == null)
			return;
		
		List<String> insertedFileNames = new LinkedList<String>();
		
		for(String ref: lst) {
			if(isFile(ref)) {
				FileInputStream in = null;
				try {
					File refFile = getFile(ref);
					in = new FileInputStream(refFile);
					
					byte[] buffer = new byte[READ_FILE_BUFFER_SIZE];
					
					String entryDir = getLocalizedString("references") + "/";
					String name = getFreeFileName(insertedFileNames, 
							refFile.getName());
					insertedFileNames.add(name);
					ZipEntry entry = new ZipEntry(entryDir + name);
					zipStream.putNextEntry(entry);
					
					int len;
					while((len = in.read(buffer)) > 0) {
						zipStream.write(buffer, 0, len);
					}
					in.close();
				} catch(IOException e) {
					try {
						if(in != null)
							in.close();
					} catch(Throwable e1) {
						
					}
					throw e;
					
				} finally {
					zipStream.closeEntry();
				}
			}
		}
	}
	
	/**
	 * Attaches review file to zip output stream
	 * @param zipStream
	 * @param lst
	 * @throws Exception 
	 */
	protected void attachReviewFile(
			ZipOutputStream zipStream, Review review) throws Exception {
		zipStream.putNextEntry(new ZipEntry(
				getLocalizedString("review") + XMLFileFilter.EXTENSION));
		
		ReviewWriter writer = new XMLReviewWriter();
		writer.write(review, new ZipStream(zipStream));
	}
	
	public void setAttachReferenceFiles(boolean attachReferenceFiles) {
		this.attachReferenceFiles = attachReferenceFiles;
	}

	public boolean isAttachReferenceFiles() {
		return attachReferenceFiles;
	}

	public void setAttachReviewFile(boolean attachReviewFile) {
		this.attachReviewFile = attachReviewFile;
	}

	public boolean isAttachReviewFile() {
		return attachReviewFile;
	}

	/**
	 * Used to write an output stream to zip output stream
	 *
	 */
	protected class ZipStream extends OutputStream implements Serializable {
		private static final long serialVersionUID = -6862359398444753838L;
		private ZipOutputStream zipStream;

		public ZipStream(ZipOutputStream zipStream) {
			this.zipStream = zipStream;
		}
		
		@Override
		public void write(int b) throws IOException {
			zipStream.write(b);
		}
		
		@Override
		public void close() throws IOException {
			zipStream.closeEntry();
			zipStream = null;
		}
	}
}
